package coder

import (
	"bytes"
	"context"
	"errors"
	"github.com/lishimeng/app-starter/stream"
)

const (
	PacketHead = 0x68
	PacketTail = 0x16

	DefaultScThreshold = 7000 // 默认短路阈值
)
const (
	CmdReqNested  = 0x01 // 嵌套命令请求
	CmdRespNested = 0x11 // 嵌套命令回复

	CmdBusReq  = 0x02 // 总线请求
	CmdBusResp = 0x12 // 总线回复

	NestCmdUIDReq  = 0x35 // 查询UID与Passwd
	NestCmdUIDResp = 0xb5 // UID&Passwd回复

	NestCmdWriteReq = 0x34 // 写入请求

	NestSubCmdUIDReq    = 0x00 // 写UID
	NestSubCmdPasswdReq = 0x07 // 写密码
	NestSubCmdDelayReq  = 0x11 // 写延时
	NestSubCmdCodeReq   = 0x1A // 写管壳码
)

type Packet struct {
	Cmd  byte
	Body []byte
}

type UidResult struct {
	uid      []byte
	password []byte
	err      error
}

type ChannelInfo struct {
	Voltage     int
	Electricity int
}

type cmdCtx struct {
	p chan string
}

var (
	ErrTimeout       = errors.New("timeout")
	ErrShortCircuit  = errors.New("短路")
	ErrBrokenCircuit = errors.New("断路")
)

type Client interface {
	OpenAll() (ci ChannelInfo, err error) // 打开所有通道 err: ErrShortCircuit
	CloseAll() (err error)                // 关闭所有通道

	GetInfo() (uid []byte, password []byte, err error)    // 读uid err: ErrBrokenCircuit
	ChannelState(ch *Channel) (ci ChannelInfo, err error) // 读总线状态

	WriteUID(uid []byte) (err error)         // 写UID
	WritePsw(password []byte) (err error)    // 写密码
	WriteDelayTime(delay []byte) (err error) // 写延时
	WriteCode(code []byte) (err error)       // 写管壳码: code需要压缩一个字节
}

type clientImpl struct {
	proxy  stream.Session
	buf    bytes.Buffer
	ctx    context.Context
	cmdCtx *cmdCtx

	shortCircuitThreshold int // 短路阈值
}

func New(ctx context.Context, portName string, baud int) (client Client, err error) {
	ch := &cmdCtx{
		p: make(chan string),
	}
	pp := stream.NewHeadTailPP([]byte{PacketHead}, []byte{PacketTail})

	p, err := stream.NewSerialSession(ctx, portName, baud,
		stream.WithReact(), stream.WithPacketProcessor(pp))
	if err != nil {
		return
	}

	c := &clientImpl{
		proxy:                 p,
		ctx:                   ctx,
		cmdCtx:                ch,
		shortCircuitThreshold: DefaultScThreshold,
	}
	pp.Listen(func(p []byte) {
		c.Handle(p)
	})
	client = c
	return
}
